namespace ConcurrentBoo

import System

enum CacheExpirationPolicy:
	
	ActiveExpiration
	"""The cache is purged by a background thread after the configured period."""
	
	PassiveExpiration
	"""Items are kept in the cache and recomputed when requested after their expiration period."""

class ComputationCache:
"""
A cache that computes requested values in the background as
they are needed.
"""
	_cache = System.Collections.Generic.Dictionary[of object, CacheItem]()
	_expirationTimeout as timespan
	
	def constructor(expirationTimeout as timespan, expirationPolicy as CacheExpirationPolicy):
		_expirationTimeout = expirationTimeout
		
	def constructor(expirationTimeout as timespan):
		self(expirationTimeout, CacheExpirationPolicy.PassiveExpiration)
		
	def constructor():
		self(timespan.FromMinutes(10))

	[lock]
	def Get(key, expression as Expression):
	"""
	Returns the cached computation if any or starts a new computation
	for the specified expression and returns immediately so multiple
	threads don't need to recompute the value.
	"""
		item as CacheItem
		if _cache.TryGetValue(key, item):
			if HasExpired(item) or item.Value.HasFinishedWithFailure:
				item.Value = future(expression)
			return item.Value
			
		value = future(expression)
		_cache.Add(key, CacheItem(Value: value))
		return value
		
	private def HasExpired(item as CacheItem):
		return (date.Now - item.LastUpdated) >= _expirationTimeout

	private class CacheItem:
		
		[getter(LastUpdated)]
		_lastUpdated as date
		
		_value as Computation
		
		Value:
			get:
				return _value
			set:
				_lastUpdated = date.Now
				_value = value
				
		
